﻿using BUGCOME.Infrastructure;
using BUGCOME.Model;
using BUGCOME.Model.Auth;
using BUGCOME.Model.Logs;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using SqlSugar.IOC;

namespace BUGCOME.ServiceCore.SqlSugar
{
    /// <summary>
    /// SqlSugar ORM框架配置工具类
    /// 负责数据库连接初始化、AOP拦截器配置、实体映射规则设置等功能
    /// </summary>
    public static class SqlsugarSetup
    {
        private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

        /// <summary>
        /// 初始化数据库连接并配置SqlSugar服务
        /// </summary>
        /// <param name="services">ASP.NET Core服务集合</param>
        /// <param name="environment">Web主机环境信息</param>
        public static void AddDb(this IServiceCollection services, IWebHostEnvironment environment)
        {
            // 从应用配置中获取数据库连接配置
            var options = App.OptionsSetting;
            List<DbConfigs> dbConfigs = options.DbConfigs;

            // 将配置转换为SqlSugar可用的连接配置列表
            var iocList = new List<IocConfig>();
            foreach (var item in dbConfigs)
            {
                iocList.Add(new IocConfig()
                {
                    ConfigId = item.ConfigId,           // 数据库配置ID
                    ConnectionString = item.Conn,       // 连接字符串
                    DbType = (IocDbType)item.DbType,    // 数据库类型
                    IsAutoCloseConnection = item.IsAutoCloseConnection  // 是否自动关闭连接
                });
            }

            // 注册SqlSugar服务到依赖注入容器
            SugarIocServices.AddSqlSugar(iocList);

            // 配置缓存服务 - 根据配置决定使用Redis缓存还是内存缓存
            ICacheService cache;
            if (options.RedisServer.DbCache)
            {
                cache = new SqlSugarRedisCache();  // 使用Redis分布式缓存
            }
            else
            {
                cache = new SqlSugarCache();       // 使用本地内存缓存
            }

            // 全局配置SqlSugar客户端
            SugarIocServices.ConfigurationSugar(db =>
            {
                // 如果当前用户已登录，应用数据权限过滤
                var u = App.User;
                if (u != null)
                {
                    DataPermi.FilterData(0);  // 对默认数据库配置应用数据权限过滤
                    //ConfigId = 1的数据权限过滤
                    //DataPermiSevice.FilterData(1);
                }

                // 为每个数据库配置设置AOP拦截器
                iocList.ForEach(iocConfig =>
                {
                    SetSugarAop(db, iocConfig, cache);
                });
            });

            // 开发环境下自动初始化数据库表结构
            if (environment.IsDevelopment())
            {
                InitTable.InitDb(options.InitDb);      // 初始化基础表结构
                InitTable.InitNewTb();                 // 初始化新增表结构
            }
        }

        /// <summary>
        /// 设置数据库连接的AOP拦截器
        /// 包括SQL执行日志、错误处理、数据变更日志和性能监控
        /// </summary>
        /// <param name="db">SqlSugar客户端实例</param>
        /// <param name="iocConfig">数据库连接配置</param>
        /// <param name="cache">缓存服务实例</param>
        private static void SetSugarAop(SqlSugarClient db, IocConfig iocConfig, ICacheService cache)
        {
            // 获取指定配置ID的连接配置
            var config = db.GetConnectionScope(iocConfig.ConfigId).CurrentConnectionConfig;
            var showDbLog = AppSettings.Get<bool>("ShowDbLog");  // 是否显示SQL日志
            string configId = config.ConfigId;

            // SQL执行前拦截 - 记录SQL语句
            db.GetConnectionScope(configId).Aop.OnLogExecuting = (sql, pars) =>
            {
                if (showDbLog)
                {
                    string log = $"【db{configId} SQL】{UtilMethods.GetSqlString(config.DbType, sql, pars)}\n";

                    // 根据SQL类型使用不同的日志级别
                    if (sql.TrimStart().StartsWith("SELECT", StringComparison.OrdinalIgnoreCase))
                    {
                        logger.Info(log);  // 查询语句使用INFO级别
                    }
                    else if (sql.StartsWith("UPDATE", StringComparison.OrdinalIgnoreCase) || sql.StartsWith("INSERT", StringComparison.OrdinalIgnoreCase))
                    {
                        logger.Warn(log);  // 更新/插入语句使用WARN级别
                    }
                    else if (sql.StartsWith("DELETE", StringComparison.OrdinalIgnoreCase) || sql.StartsWith("TRUNCATE", StringComparison.OrdinalIgnoreCase))
                    {
                        logger.Error(log);  // 删除/截断语句使用ERROR级别
                    }
                    else
                    {
                        // 其他类型SQL语句的日志格式化
                        log = $"【db{configId} SQL语句】dbo.{sql} {string.Join(", ", pars.Select(x => x.ParameterName + " = " + GetParsValue(x)))};\n";
                        logger.Info(log);
                    }
                }
            };

            // SQL执行错误拦截 - 记录错误信息
            db.GetConnectionScope(configId).Aop.OnError = (ex) =>
            {
                string sql = "【错误SQL】" + UtilMethods.GetSqlString(config.DbType, ex.Sql, (SugarParameter[])ex.Parametres) + "\r\n";
                logger.Error(ex, $"{sql}\r\n{ex.Message}\r\n{ex.StackTrace}");
            };

            // 数据执行前拦截 - 可用于数据转换或验证
            db.GetConnectionScope(configId).Aop.DataExecuting = (oldValue, entiyInfo) =>
            {
                // 此处可添加数据执行前的处理逻辑
            };

            // 差异日志功能 - 记录数据变更前后的状态
            db.GetConnectionScope(configId).Aop.OnDiffLogEvent = it =>
            {
                // 操作前记录  包含： 字段描述 列名 值 表名 表描述
                var editBeforeData = it.BeforeData;  // 插入操作时Before为null
                // 操作后记录   包含： 字段描述 列名 值  表名 表描述
                var editAfterData = it.AfterData;
                var sql = it.Sql;
                var parameter = it.Parameters;
                var data = it.BusinessData;  // 业务对象数据
                var time = it.Time;
                var diffType = it.DiffType;  // 变更类型：insert、update、delete
                string name = App.UserName;  // 当前操作用户

                // 记录每条变更数据
                foreach (var item in editBeforeData)
                {
                    var pars = db.Utilities.SerializeObject(item.Columns.ToDictionary(it => it.ColumnName, it => it.Value));

                    SqlDiffLog log = new()
                    {
                        BeforeData = pars,
                        BusinessData = data?.ToString(),
                        DiffType = diffType.ToString(),
                        Sql = sql,
                        TableName = item.TableName,
                        UserName = name,
                        AddTime = DateTime.Now,
                        ConfigId = configId
                    };

                    // 如果有变更后数据，记录变更后的值
                    if (editAfterData != null)
                    {
                        var afterData = editAfterData?.First(x => x.TableName == item.TableName);
                        var afterPars = db.Utilities.SerializeObject(afterData?.Columns.ToDictionary(it => it.ColumnName, it => it.Value));
                        log.AfterData = afterPars;
                    }

                    // 将变更日志写入数据库
                    db.GetConnectionScope(0)
                      .Insertable(log)
                      .ExecuteReturnSnowflakeId();
                }
            };

            // 配置连接更多设置
            db.GetConnectionScope(configId).CurrentConnectionConfig.MoreSettings = new ConnMoreSettings()
            {
                IsAutoRemoveDataCache = true  // 自动移除数据缓存
            };

            // 配置外部服务
            db.GetConnectionScope(configId).CurrentConnectionConfig.ConfigureExternalServices = new ConfigureExternalServices()
            {
                DataInfoCacheService = cache,  // 设置缓存服务

                // 实体属性配置 - 控制属性到列的映射规则
                EntityService = (c, p) =>
                {
                    // 主键不可为空
                    if (p.IsPrimarykey == true)
                    {
                        p.IsNullable = false;
                    }
                    // 标记为NOTNULL的属性不可为空
                    else if (p.ExtendedAttribute?.ToString() == ProteryConstant.NOTNULL.ToString())
                    {
                        p.IsNullable = false;
                    }
                    // 其他属性默认可为空
                    else
                    {
                        p.IsNullable = true;
                    }

                    // PostgreSQL特定配置
                    if (config.DbType == DbType.PostgreSQL)
                    {
                        // 特定布尔类型字段使用char(1)存储
                        if (c.Name == nameof(Menu.IsCache) || c.Name == nameof(Menu.IsFrame))
                        {
                            p.DataType = "char(1)";
                        }
                    }

                    // Oracle兼容性配置
                    if (config.DbType == DbType.Oracle)
                    {
                        // 自增列配置序列名称
                        if (p.IsIdentity == true)
                        {
                            if (p.EntityName == nameof(UserBase))
                            {
                                p.OracleSequenceName = "SEQ_SYS_USER_USERID";
                            }
                            else if (p.EntityName == nameof(Menu))
                            {
                                p.OracleSequenceName = "SEQ_SYS_MENU_MENUID";
                            }
                            else
                            {
                                p.OracleSequenceName = "SEQ_ID";
                            }
                        }
                    }
                }
            };

            // SQL执行后拦截 - 监控执行时间
            db.GetConnectionScope(configId).Aop.OnLogExecuted = (sql, pars) =>
            {
                var sqlExecutionTime = AppSettings.Get<int>("sqlExecutionTime");

                // 记录超过设定阈值的SQL执行
                if (db.Ado.SqlExecutionTime.TotalSeconds > sqlExecutionTime)
                {
                    // 获取执行SQL的代码位置信息
                    var fileName = db.Ado.SqlStackTrace.FirstFileName;     // 代码文件名
                    var fileLine = db.Ado.SqlStackTrace.FirstLine;         // 代码行号
                    var FirstMethodName = db.Ado.SqlStackTrace.FirstMethodName;  // 方法名

                    var logInfo = $"Sql执行超时，用时{db.Ado.SqlExecutionTime.TotalSeconds}秒【{sql}】,fileName={fileName},line={fileLine},methodName={FirstMethodName}";
                    logger.Warn(logInfo);  // 记录超时SQL
                }
            };
        }

        /// <summary>
        /// 格式化SQL参数值，用于日志记录
        /// </summary>
        /// <param name="x">SQL参数</param>
        /// <returns>格式化后的参数值</returns>
        private static object GetParsValue(SugarParameter x)
        {
            // 字符串和日期类型的值添加引号
            if (x.DbType == System.Data.DbType.String || x.DbType == System.Data.DbType.DateTime || x.DbType == System.Data.DbType.String)
            {
                return "'" + x.Value + "'";
            }
            return x.Value;
        }
    }
}